/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Klassen;

import Enums.RowType;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 
 * @author Serenity
 */
public class Board {
    private Field[][] fields;                                                   //Array zum registrieren der einzelnen Felder
    private Row[] yRows;                                                        //Array zum registrieren der Reihen der Y-Achse (vertikal)
    private Row[] xRows;                                                        //Array zum registrieren der Reihen der X-Achse (horizontal)
    private Box[] boxes;                                                        //Array zum registrieren der 3x3 Boxen
    private boolean solved = false;                                             //Feld gelöst? 
    private int stepThree_Level = 0;
    private ArrayList<ArrayList<Field>> stack = null;
    
    private int usageStepOne;
    private int usageStepOPF;
    private int usageStepTwo;
    private int usageStepThree;
    private int usageDrawConclusion;
    private int newStateInStepOne;
    private int newStateInStepOPF;
    private int newStateInStepTwo;
    private int newStateInStepThree;
    private int fieldSetByStepOne;
    private int fieldSetByStepOPF;
    private int fieldSetByStepThree;
    private int rollbackFields;
    
    private boolean isManual;
    private boolean isBenchmark;
    private int border = 1265;
    
    
    public Board() {                                                            //Constructor => Initialisierung der Arrays
        this.fields = new Field[9][9];
        this.xRows = new Row[9];
        this.yRows = new Row[9];
        this.boxes = new Box[9];
        this.stack = new ArrayList<ArrayList<Field>>();
    }
    
    private void statistic() {
        System.out.println("StepOne: Usage: " + this.usageStepOne + ", new State: " + this.newStateInStepOne + ", fields set: " + this.fieldSetByStepOne);
        System.out.println("StepOPF: Usage: " + this.usageStepOPF + ", new State: " + this.newStateInStepOPF + ", fields set: " + this.fieldSetByStepOPF);
        System.out.println("StepTwo: Usage: " + this.usageStepTwo + ", new State: " + this.newStateInStepTwo);
        System.out.println("StepThree: Usage: " + this.usageStepThree + ", new State: " + this.newStateInStepThree + ", fields set: " + this.fieldSetByStepThree);
        System.out.println("DrawConclusion: Usage: " + this.usageDrawConclusion);
        System.out.println("RollbackFields: " + this.rollbackFields);
    }
    
    private void initializeBoard(int puzzle) throws IOException {                                            //Methode zur Initialisierung des Spielfeldes
        int[][] values = this.load(puzzle);                                           //Aufruf zum Laden eines Sudokus
        
        this.solved = false;
        this.stepThree_Level = 0;
        this.stack.clear();
            
        //<<Bench
        if(!this.isBenchmark){
            
            usageStepOne = 0;
            usageStepOPF = 0;
            usageStepTwo = 0;
            usageStepThree = 0;
            usageDrawConclusion = 0;
            newStateInStepOne = 0;
            newStateInStepOPF = 0;
            newStateInStepTwo = 0;
            newStateInStepThree = 0;
            fieldSetByStepOne = 0;
            fieldSetByStepOPF = 0;
            fieldSetByStepThree = 0;
            rollbackFields = 0;
        }
        
        //<<Bench
        
        for(int i=0; i<9; i++) {                                                //Anlegen der Reihen und Boxen
            xRows[i] = new Row(this, i, RowType.horizontal);
            yRows[i] = new Row(this, i, RowType.vertical);
            boxes[i] = new Box(this, i);
        }
        for(int x=0; x<9; x++) {                                                //Anlegen der einzelnen Felder und Registrierung dieser in den entsprechenden Reihen und Boxen
            for(int y=0; y<9; y++) {
                int boxOffset_x = x/3;
                int boxOffset_y = y/3;
                int boxPos = boxOffset_y*3+boxOffset_x;
                this.fields[x][y] = new Field(x, y, this.xRows[x], this.yRows[y], this.boxes[boxPos], values[x][y]);
                this.xRows[x].fields[y] = this.fields[x][y];
                this.yRows[y].fields[x] = this.fields[x][y];
                int fieldOffset_x = x%3;
                int fieldOffset_y = y%3;
                int fieldPos = fieldOffset_y*3+fieldOffset_x;
                this.boxes[boxPos].fields[fieldPos] = this.fields[x][y];
            }
        }
        this.new_missing_values();                                               //Aufruf der Methode zum erzeugen der Listen mit den möglichen Werten für alle Felder
    }
    
    private void new_missing_values() {
        for(int i=0; i<9; i++) {                                                //Aufruf zum erzeugen der Listen mit den fehlenden Werten für die Reihen und Boxen
            xRows[i].generate_missing_values();
            yRows[i].generate_missing_values();
            boxes[i].generate_missing_values();
        }
        this.new_posVal_fields();
    }
    
    private void new_posVal_fields() {                                          //Methode zum erzeugen der Listen mit den möglichen Werten für alle Felder
        for(int x=0; x<9; x++) {
            for(int y=0; y<9; y++) {
                
                this.fields[x][y].generate_possible_values();
            }
        }
    }
    
    private void check_if_solved() {                                            //Methode zum prüfen ob das Spiel gelöst ist
        boolean isSolved = true;                                                //=> Prüfen ob alle Reihen und Boxen gelöst sind
        for(int i=0; i<9; i++) {
            if(!this.xRows[i].solved) {
                isSolved = false;
                break;
            }
            if(!this.yRows[i].solved) {
                isSolved = false;
                break;
            }
            if(!this.boxes[i].solved) {
                isSolved = false;
                break;
            }
        }
        if(isSolved) {
            this.solved = true;
            
            if(!this.isBenchmark){
                System.out.println("Solved!");
            }
        }
    }
    
    private int[][] load(int puzzle) throws IOException {                                                    //Methode zum Laden eines Sudokus
        //SudokuGetter.getSudoku("test.csv"); //
        SudokuConverter.getSudoku("source.txt", puzzle); //integer between 0-16200  //10324, 401
        return CSVReader.ReadCSV("test.csv");
    }
    
    public boolean start() throws IOException {                                                    //Methode zum Start eines Spielzykluses
        this.initializeBoard(10324);

        boolean calc = this.calculate();
        this.printBoard();
        this.statistic();
        
        if(calc) {
            System.out.println("Calculate successful");
        } else {
            System.out.println("Calculate unsuccessful");
        }
        
        return calc;
    }
    
    public boolean benchmark() throws IOException {
        int noSolved = 0;
        int noFailed = 0;
        long start = System.currentTimeMillis();
        long stop;
        
        this.isBenchmark = true;
        
        
        
        for(int i = 0; i<=this.border; i++) {
            this.initializeBoard(i);
            boolean calc = this.calculate();
            //this.printBoard();
            
            
            if(calc) {
                noSolved++;
                System.out.println(i + " solved.");
            } else {
                noFailed++;
                System.out.println(i + " failed.");
            }
        }
        
        stop = System.currentTimeMillis();
        int seconds = (int)(stop-start)/1000;
        
        System.out.println("======================");
        System.out.println(noSolved + " puzzles solved");
        System.out.println(noFailed + " puzzles failed");
        System.out.println("Total Time: "+ seconds );
        System.out.println("Sudoku/Second: "+(16201/seconds)+"\n");
        this.statistic();
        
        return true;
    }
 
    
    private boolean calculate() throws IOException {                                               //Methode zur Steuerung der Berechnung => Makro KI / Dachalgo
        int step = 1;                                                           //Variable zum Festhalten, welcher Unteralgo im nächsten Durchlauf angewendet werden soll
        this.check_if_solved();
        do {
            
            StepResult stepResult;                                              //Objekt zum Festhalten der Ergebnisse eines Unteralgo Durchlaufes
            switch(step) {                                                      //Entscheidung welcher Unteralgo angewendet wird und Aufruf dessen
                case 1: stepResult = this.stepOne();
                        
                        break;
                case 2: stepResult = this.stepOnePointFive();
                        
                        break;
                case 3: stepResult = this.stepTwo();
                        
                        break;
                case 4:
                        stepResult = this.drawConclusion();
                        break;
                    
                case 5: stepResult = this.stepThree();
                        
                        break;
                default:stepResult = new StepResult();
            }
            if(stepResult.isError()) {                                          //Überprüfung ob ein Fehler aufgetreten ist => Rausspringen aus der Methode
                if(!this.isBenchmark){
                    System.out.println("<< Error in case " + step + " on level " + this.stepThree_Level);
                }
                return false;
            }
            else if(stepResult.isNew_step()) {                                       //Überprüfen ob sich ein oder mehrere Felder im Unteralgo geändert haben
                if(!this.isBenchmark){
                    System.out.println("<< New step in case " + step + ". Jump to stepOne");
                }
                step = 1;                                                       //Wenn ja, Unteralgo 1 im nächsten Durchlauf nutzen und prüfen ob Rätsel gelöst
                this.check_if_solved();
            } else {
                if(!this.isBenchmark){
                    System.out.println("<< No new step in case " + step + ". Next step!!!");
                }
                step++;                                                         //Wenn nein, nächsthöheren Unteralgo beim nächsten Durchlauf aufrufen
            }
            this.new_posVal_fields();
            
            if(!this.isBenchmark && !this.isManual){
                this.printBoard();
            }
            //erstmal behelfsmäßig. Prüfen was an dieser Stelle nötig sein könnte
            if(step>5 || step<0) {
                this.check_if_solved();
                
                if(!this.solved){
                    //System.out.print("Could not finish this puzzle!");
                    return false;
                }
            }
            
            
            if(this.isManual){
                System.in.read();
            }
            
        } while(!this.solved);                                   //Wiederholen der Schritte solange das Rätsel nicht gelöst ist bzw. kein Fehler aufgetreten ist
        //System.out.print("Board ist fully solved!");
        //this.printBoard();
        
        return true;
    }
    
    private StepResult stepOne() throws IOException {                                              //Erster und einfachster der Unteralgos
        if(!this.isBenchmark){
            System.out.println("Start stepOne");
        }
        
        this.usageStepOne++;
        boolean new_state = false;                                              //Änderung eines Feldes oder mehrerer Felder verursacht?
        boolean error_occured = false;                                          //Auftritt eines Fehlers beim Lösen?
        for (int x=0; x<9; x++) {                                               //Durchgehen aller 9*9 Felder
            for(int y=0; y<9; y++) {
                if(x==8 && y==2){
                    int ui = 9;
                }
                if(this.fields[x][y].getValue() == 0                            //Wenn das Feld noch nicht gelöst ist (Wert = 0) und es nur einen Wertevorschlag gibt den Vorschlag als Wert für das Feld festlegen
                        && this.fields[x][y].getPossible_values().size() == 1) {
                    error_occured = !this.fields[x][y].setValue(this.fields[x][y].getPossible_values().get(0));
                    if(error_occured) {
                        return new StepResult(false, error_occured);
                    }
                    new_state = true;
                    this.fieldSetByStepOne++;
                    
                    if(!this.isBenchmark){
                        System.out.println("Field " + x + " " + y + " new Value: " + this.fields[x][y].getValue());
                        
                        
                    }
                    
                    if(this.isManual){
                        this.printBoard();
                        System.in.read();
                    }
                    
                    if(this.stepThree_Level > 0){
                        this.stack.get(stepThree_Level - 1).add(this.fields[x][y]);
                    }
                }
                if(this.fields[x][y].getValue() == 0 && this.fields[x][y].getPossible_values().isEmpty()) {
                    return new StepResult(false, true);
                }
                
            }
        }
        if(new_state) {
            this.newStateInStepOne++;
        }
        return new StepResult(new_state, error_occured);                        //Erzeugen und Rückgabe eines stepResult Objektes mit den Ergebnissen des Unteralgos
    }
    
    private StepResult stepOnePointFive() throws IOException {                                              //Zweiter Unteralgo
        if(!this.isBenchmark){
            System.out.println("Start stepOnePointFive");
        }
        
        this.usageStepOPF++;
        boolean new_state = false;                                              //Änderung eines Feldes oder mehrerer Felder verursacht?
        boolean error_occured = false;                                          //Auftritt eines Fehlers beim Lösen?
        
        ArrayList<FieldContainer[]> fieldContainer = new ArrayList<FieldContainer[]>();     //Organisation aller Reihen und Boxen in einer ArrayList
        fieldContainer.add(this.xRows);
        fieldContainer.add(this.yRows);
        fieldContainer.add(this.boxes);
        
        for(int fC=0; fC<fieldContainer.size(); fC++) {                         //Durchgehen aller FieldContainerArrays (= aller Reihen und Boxen)
            if(fC > 0) {
                this.new_posVal_fields();
            }
            FieldContainer[] accFC = fieldContainer.get(fC);
            for(int i=0; i<9; i++){                                             //Durchgehen aller Objekte im aktuell ausgewählten FieldContainer.
                
                //--------------------------------------
                
                    //ArrayList<Integer> missingValuesClone = new ArrayList<Integer>(accFC[i].getMissing_values());
                
                    //for(int vV=0; vV<missingValuesClone.size(); vV++) {                                       //Durchgehen aller Zahlen (Vorschläge)
                    
                    //    Integer v = missingValuesClone.get(vV);
                
                //--------------------------------------
                    
                for(int v=1; v<=9; v++) {
                    boolean alreadySet = false;
                    int noOfOcc = 0;
                    Field Occ = null;                   
                    for(int f=0; f<9; f++) {                                    //Durchgehen aller Felder für diesen Vorschlag
                        if(!alreadySet) {
                            if(accFC[i].fields[f].getPossible_values().contains(new Integer(v))) {
                                noOfOcc++;
                                Occ = accFC[i].fields[f];
                            }
                            if(accFC[i].fields[f].getValue() == v){
                                alreadySet = true;
                                break;
                            }
                        }        
                    }
                    if(noOfOcc == 1 && !alreadySet) {
                        if(!this.isBenchmark){
                            System.out.println("Setting " + Occ.getPosition_x() + " " + Occ.getPosition_y() + " to "+v);
                        }
                        
                        if(this.isManual){
                            this.printBoard();
                            System.in.read();
                        }
                        
                        error_occured = !Occ.setValue(v);                       //TODO geändert!!!
                        new_state = true; //MORITZ changed this shit
                        this.fieldSetByStepOPF++;
                        if(this.stepThree_Level > 0){
                            this.stack.get(stepThree_Level - 1).add(Occ);
                        }
                    }
                    if((noOfOcc == 0 && !alreadySet) || error_occured) {
                        if(noOfOcc == 0 && !alreadySet) {
                            if(!this.isBenchmark){
                                System.out.println("noOfOcc == 0 && !alreadySet, fC: " + fC + ", accFC: " + i + ", v: " + v);
                            }
                            
                            if(this.isManual){
                                this.printBoard();
                                System.in.read();
                            }
                        } else {
                            if(!this.isBenchmark){
                                System.out.println("");
                            }
                        }
                        return new StepResult(false, true);
                    }
                }
            }
        }
        if(new_state) {
            this.newStateInStepOPF++;
        }
        return new StepResult(new_state, error_occured);
    }
    
    private StepResult stepTwo() throws IOException {                                              //Zweiter Unteralgo
        if(!this.isBenchmark){
            System.out.println("Start stepTwo");
        }
        
        this.usageStepTwo++;
        boolean new_state = false;                                              //Änderung eines Feldes oder mehrerer Felder verursacht?
        boolean error_occured = false;                                          //Auftritt eines Fehlers beim Lösen?
        
        ArrayList<FieldContainer[]> fieldContainer = new ArrayList<FieldContainer[]>();     //Organisation aller Reihen und Boxen in einer ArrayList
        fieldContainer.add(this.xRows);
        fieldContainer.add(this.yRows);
        fieldContainer.add(this.boxes);
        
        for(int fC=0; fC<fieldContainer.size(); fC++) {                         //Durchgehen aller FieldContainerArrays (= aller Reihen und Boxen)
            FieldContainer[] accFC = fieldContainer.get(fC);
            for(int i=2; i<=4; i++) {                                           //Anzahl an zu findenden Werte- und Vorschlagspaaren
                for(int x=0; x<9; x++) {                                        //Durchgehen der einzelnen Reihen/Boxen des spezifischen Arrays
                    ArrayList<Field> pos_fields = accFC[x].fields_for_stepTwo(i);       //Anfordern aller aufgrund der Anzahl an vorgeschlagenen Werten infragekommender Felder der Reihe/Box
                    if(pos_fields.size() >= i) {
                        ArrayList<Field> hitFields = this.stepTwo_match_fields_basic(pos_fields);   //Bei Rückgabe von gleich oder mehr Felder als benötigt Aufruf einer Methode zum Überprüfen, ob diese von den vorgeschlagenen Werten auch übereinstimmen
                        if(null != hitFields) {
                            for(int j=0; j<9; j++) {
                                if(accFC[x].fields[j].getValue() == 0) {
                                    if(!hitFields.contains(accFC[x].fields[j])) {   //Bei Rückgabe von Feldern zur Reduzierung der Wertevorschläge anderer Felder der Reihe/Box Streichen der entsprechenden Werte aus der Wertevorschlagslisten dieser
                                        for(int r=0; r<hitFields.get(0).getPossible_values().size(); r++) {
                                            if(accFC[x].fields[j].getPossible_values().contains(hitFields.get(0).getPossible_values().get(r))) {
                                                if(!this.isBenchmark){
                                                    System.out.println("Deleting posVals "+hitFields.get(0).getPossible_values()+" in "+accFC[x].fields[j].getPosition_x()+" "+accFC[x].fields[j].getPosition_y()+" "+accFC[x].fields[j].getPossible_values());
                                                }
                                                accFC[x].fields[j].newSpecialRemovedValues(hitFields.get(0).getPossible_values().get(r));
                                                new_state = true;
                                                
                                                if(this.isManual){
                                                    System.in.read();
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            if(new_state && accFC[0].getClass().equals(Row.class)) {        //Falls es sich um eine Reihe handelt, Aufruf einer Methode zum Überprüfen, ob auch Einfluss auf die Wertevorschlagslisten einer Box bestehen könnten.
                                //accFC.getClass() gegen accFC[0].getClass() ausgetauscht, damit die Instanz von Row und nicht die Instanz vom Array abgefragt wird
                                
                                this.stepTwo_match_fields_rows(hitFields);

                            }
                        }
                    }
                }          
            }    
        }
        if(new_state) {
            this.newStateInStepTwo++;
        }
        return new StepResult(new_state, error_occured);
    }
    
    private void stepTwo_match_fields_rows(ArrayList<Field> fields) throws IOException {           //Methode zum Überprüfen, ob alle zum Reduktionsschritt nötigen Felder in einer Box liegen. Falls ja, Reduktion der Wertevorschlagslisten der entsprechenden Felder
        boolean sameBox = true;
        
        Box box = fields.get(0).getBox();
        for(int i=1; i<fields.size(); i++) {                                    //Durchgehen der Boxnummern der anderen Felder und Vergleich mit der des ersten Feldes
            if(fields.get(i).getBox() != box) {
                sameBox = false;
            }
        }
        if(sameBox) {                                                           //Bei Einfluss auf eine Box Reduzierung der Wertevorschlagslisten aller entprechender Felder 
            for(int i=0; i<9; i++) {
                if(!fields.contains(box.fields[i])){
                    if(!this.isBenchmark){
                        System.out.println("Deleting posVals "+fields.get(0).getPossible_values()+" in "+box.fields[i].getPosition_x()+" "+box.fields[i].getPosition_y()+" "+box.fields[i].getPossible_values());
                    }
                    
                    if(this.isManual){
                        System.in.read();
                    }
                    
                    for(int j=0; j<fields.get(0).getPossible_values().size(); j++) {
                        if(box.fields[i].getPossible_values().contains(fields.get(0).getPossible_values().get(j))) {
                            box.fields[i].newSpecialRemovedValues(fields.get(0).getPossible_values().get(j));
                        }
                    }
                }
            }
        }
    }
    
    private ArrayList<Field> stepTwo_match_fields_basic(ArrayList<Field> pos_fields) {  //Methode zur Überprüfung, ob die Wertevorschläge einzelner Felder mit der gleichen Anzahl an Wertevorschlägen in ihren Wertevorschlagslisten auch übereinstimmen
        int hits_needed = pos_fields.get(0).getPossible_values().size();        //Benötigte Anzahl an übereinstimmenden Feldern
        ArrayList<Field> returnFields = new ArrayList<Field>();                 //ArrayListe zur Rückgabe der Felder mit übereinstimmenden Wertevorschlagslisten
        for(int i=0; i<=pos_fields.size()-hits_needed; i++) {                   //Auswahl des Feldes als Vergleichspunkt
            int hits_found = 1;                                                 //Variable zum Speichern der Anzahl der Felder mit übereinstimmenden Wertevorschlagslisten. Initial auf 1, da das Vergleichspunktfeld hier auch mitgezählt wird
            returnFields.add(pos_fields.get(i));                                //Hinzufügen des Vergleichspunktfeldes zu den Rückgabefeldern
            for(int j=i+1; j<pos_fields.size(); j++) {                          //Durchgehen aller Felder der ArrayListe mit höherem Index als das Vergleichsfeld
                boolean same = true;
                for(int v=0; v<hits_needed; v++) {                              //Vergleich der einzelnen Werte der Wertevorschlagslisten
                    if(!pos_fields.get(j).getPossible_values().contains(pos_fields.get(i).getPossible_values().get(v))) {
                        same = false;
                        break;
                    }
                }
                if(same) {                                                      //Bei Übereinstimmung der Wertevorschlagslisten hinzufügen des Feldes zu den Rückgabefeldern
                    hits_found++;
                    returnFields.add(pos_fields.get(j));
                }
            }
            if(hits_needed == hits_found) {                                     //Stimmen die Wertevorschlagslisten von genau so viele Felder wie benötigt überein, Rückgabe dieser Rückgabefelder
                return returnFields;
            }
            returnFields.clear();                                               //Ansonsten Bereinigen der Rückgabefelder
        }
        return null;                                                            //Falls bei Durchlauf aller Felder kein Erfolg, Rückgabe von NULL
    }
    
    private StepResult stepThree() throws IOException {                                //Zweiter Unteralgo
        if(!this.isBenchmark){
            System.out.println("Start stepThree");
        }
        this.usageStepThree++;
        this.stepThree_Level++;
        this.stack.add(new ArrayList<Field>());
        boolean rightEstimation = true;
        
        
        Field field = low_Pos_Vals();
        int estimation = field.getPossible_values().get(0);
        field.setValue(estimation);
        this.fieldSetByStepThree++;
        this.newStateInStepThree++;
        
        this.stack.get(stepThree_Level - 1).add(field);
        
        for(int x=0; x<9; x++) {
            for(int y=0; y<9; y++) {
                this.fields[x][y].new_level();
            }
        }
        
        this.new_missing_values();
        
        if(!this.isBenchmark){
            System.out.println("Estimating "+estimation+" from the posVals "+field.getPossible_values()+" to be the next Value for "+field.getPosition_x()+" "+field.getPosition_y());
        }
        
        rightEstimation = this.calculate();
        
        if(rightEstimation){
            return new StepResult(true, false);
        } else {
            if(this.stepThree_Level == 0) {
                return new StepResult(false, true);
            }
            
            if(!this.isBenchmark){
                System.out.println("Rollback auf Level " + (stepThree_Level-1));
            }
            
            ArrayList<Field> levelStack = this.stack.get(this.stepThree_Level-1);
            for(int i=0; i<levelStack.size(); i++){
                levelStack.get(i).rollback();
                rollbackFields++;
            }
            for(int x=0; x<9; x++) {
                for(int y=0; y<9; y++) {
                    this.fields[x][y].delete_level();
                }
            }
            this.stack.remove(stepThree_Level-1);
            this.stepThree_Level--;
            field.newSpecialRemovedValues(new Integer(estimation));
            this.new_missing_values();
            return new StepResult(true, false);
        }
    }
    
    
    private StepResult drawConclusion() throws IOException{
        if(!this.isBenchmark){
            System.out.println("Drawing Conclusions");
        }
        
        this.usageDrawConclusion++;
        
        boolean newStep = false;
        this.new_missing_values();
        this.new_posVal_fields();
        
        for(int boxIndex = 0; boxIndex < 9; boxIndex++){
      
            
            ArrayList<Integer> toDelete = new ArrayList<Integer>();
            Hashtable<Integer,ArrayList<Row>> posValsx = new Hashtable<Integer, ArrayList<Row>>(); //Welche Reihen gehören zu welchem PosVal
            Hashtable<Integer,ArrayList<Row>> posValsy = new Hashtable<Integer, ArrayList<Row>>(); //Welche Reihen gehören zu welchem PosVal
            
            //Welche Posvals liegen in der kompletten box?
            for(int fieldIndex = 0; fieldIndex < 9; fieldIndex++){
                
                //Iteriere über die Posvals
                for(Integer pV : this.boxes[boxIndex].fields[fieldIndex].getPossible_values()){
                    
                    if(posValsx.containsKey(pV)){
                        
                        posValsx.get(pV).add(this.boxes[boxIndex].fields[fieldIndex].getxRow());
                        
                    }else{
                        ArrayList<Row> rowx = new ArrayList<Row>();
                        
                        rowx.add(this.boxes[boxIndex].fields[fieldIndex].getxRow());
                        
                        posValsx.put(pV, rowx);
                    }
                    
                    if(posValsy.containsKey(pV)){
                        
                        posValsy.get(pV).add(this.boxes[boxIndex].fields[fieldIndex].getyRow());
                        
                    }else{
                        ArrayList<Row> rowy = new ArrayList<Row>();
                        
                        rowy.add(this.boxes[boxIndex].fields[fieldIndex].getyRow());
                        
                        posValsy.put(pV, rowy);
                    }
                }
            }
            
            for(int posVal = 1; posVal<=9; posVal++){
                
                boolean isUnique = true;
                Row rowID = null;
                
                if(posValsx.containsKey(posVal)){
                    
                    rowID = posValsx.get(posVal).get(0);
                    
                    for(int row = 1; row<posValsx.get(posVal).size(); row++){
                        
                        if(!rowID.equals(posValsx.get(posVal).get(row))){
                          isUnique = false;
                          break;
                        }
                    }
                    
                    //Delete every occurence in this row
                    if(isUnique){
                        newStep = newStep || rowID.removePosVal(posVal, boxIndex, this.isManual,this.isBenchmark);
                    }
                }
                
                isUnique = true;
                
                if(posValsy.containsKey(posVal)){
                    
                    rowID = posValsy.get(posVal).get(0);
                    
                    for(int row = 1; row<posValsy.get(posVal).size(); row++){
                        
                        if(!rowID.equals(posValsy.get(posVal).get(row))){
                          isUnique = false;
                          break;
                        }
                    }
                    
                    //Delete every occurence in this row
                    if(isUnique){
                        newStep = newStep || rowID.removePosVal(posVal, boxIndex, this.isManual,this.isBenchmark);
                    }
                }
            }
            
        }
        
        //Am schluss muss calculate manuell aufgerufen werden, damit die Posvals nicht neu berechnet werden.
        //this.calculate();
        
        return (new StepResult(newStep,false));
    }
    
    /////////////////////////////// Dummys etc.
    
    public void printBoard() {
        //=========================
        //| 0 1 5 | 0 0 9 | 0 2 0 |
        //| 7 3 2 | 0 0 1 | 8 9 5 |
        //| 8 6 9 | 0 0 5 | 0 1 3 |
        //=========================
        //| 6 2 0 | 1 0 3 | 5 4 9 |
        //| 0 7 4 | 5 0 8 | 0 0 0 |
        //| 0 0 3 | 2 4 6 | 7 8 1 |
        //=========================
        //| 0 0 0 | 9 0 4 | 2 7 0 |
        //| 2 4 1 | 0 3 7 | 0 0 6 |
        //| 0 8 0 | 6 0 0 | 0 3 0 |
        //=========================
        
        System.out.println("=========================");
        for(int y=0; y<9; y++) {
            StringBuilder sb = new StringBuilder("|");
            for(int x=8; x>=0; x--) {
                sb.append(" ");
                sb.append(this.fields[y][x].getValue());
                if(x%3==0) {
                    sb.append(" |");
                }
            }
            System.out.println(sb);
            if(y%3==2) {
                System.out.println("=========================");
            }
        }
    }
    
    public void printFieldContainer(FieldContainer[] toPrint) {
        for(int i=0; i<9; i++) {
            toPrint[i].print_suggestions();
        }
    }
    
    public void printFields() {
        for(int x=0; x<9; x++) {
            for(int y=0; y<9; y++) {
                this.fields[x][y].print_field_data();
            }
        }
    }
    
    public int[][] dummyLoading() {
        int[][] loaded_values = {
            {0, 1, 5, 0, 0, 9, 0, 2, 0},
            {7, 3, 2, 0, 0, 1, 8, 9, 5},
            {8, 6, 9, 0, 0, 5, 0, 1, 3},
            {6, 2, 0, 1, 0, 3, 5, 4, 9},
            {0, 7, 4, 5, 0, 8, 0, 0, 0},
            {0, 0, 3, 2, 4, 6, 7, 8, 1},
            {0, 0, 0, 9, 0, 4, 2, 7, 0},
            {2, 4, 1, 0, 3, 7, 0, 0, 6},
            {0, 8, 0, 6, 0, 0, 0, 3, 0}    
        };
        return loaded_values;
    }
    
    private Field low_Pos_Vals(){
        
        Field posValField = this.fields[8][8];  //Erstes sudoku Feld bei 0,0
        
        
        for (byte x = 0; x < 9; x++){
            for(byte y = 0; y < 9; y++){
                if((posValField.getPossible_values().size() > this.fields[x][y].getPossible_values().size() || posValField.getPossible_values().isEmpty()) && !this.fields[x][y].getPossible_values().isEmpty() && this.fields[x][y].getValue() == 0){ //Prüfen, ob posVal von posValField größer als das gerade ausgewählte Feld. Bei Übereinstimmung wird posValField überschrieben
                    posValField = this.fields[x][y];
                }
            }
        }
        
        return posValField;
    }

    public void startAutomatic(int sudokuNr) throws IOException {
        System.out.println("\nStarting Sudoku Nr."+sudokuNr+"\n");
        
        this.isManual = false;
        this.isBenchmark = false;
        
        this.initializeBoard(sudokuNr);
        
        boolean calc = this.calculate();
        this.printBoard();
        this.statistic();
        
        if(calc) {
            System.out.println("Calculate successful");
        } else {
            System.out.println("Calculate unsuccessful");
        }
        
        this.solved = false;
    }

    public void starManual(int sudokuNr) throws IOException {
        System.out.println("\nStarting Sudoku Nr."+sudokuNr+"\n");
        
        this.isManual = true;
        this.isBenchmark = false;
        
       
        this.initializeBoard(sudokuNr);
        
        
        boolean calc = this.calculate();
        this.printBoard();
        this.statistic();
        
        if(calc) {
            System.out.println("Calculate successful");
        } else {
            System.out.println("Calculate unsuccessful");
        }
        
        
        this.solved = false;
    }
}
